#ifndef _NE_PARTICLE_SYSTEM_H_
#define _NE_PARTICLE_SYSTEM_H_

#include <partical/ParticleEmitter.h>
#include <partical/ParticleAffector.h>
#include <partical/ParticleFadeAffector.h>
#include <partical/ParticleGravityAffector.h>

#include <mathematics/AABB.h>
#include <graphics/Camera.h>
#include <algorithm/RadixSort32.h>

namespace ne
{
    class NEAPI ParticleSystem : public Node
    {
        public:
            ParticleSystem();

            virtual void setSpeed(const real speed);
            virtual real getSpeed() const;

            virtual void setSortEnable(const bool b);
            virtual bool isSortEnable() const;

            virtual void setSortMode(const SortMode &sm);
            virtual SortMode getSortMode() const;

            virtual void setParticlePoolSize(const size_t size);

            virtual size_t getParticlePoolSize() const;

            virtual Particle* createParticle();

            virtual size_t getParticleNum() const;

            virtual Particle* getParticle(const size_t index);

            virtual ParticleList& getParticleList();

            virtual void clearParticles();

            // Emitted Emitter
            virtual ParticleEmitter* createEmittedEmitter();

            virtual size_t getEmittedEmitterNum() const;

            virtual ParticleEmitter* getEmittedEmitter(const size_t index);

            virtual ParticleEmitterList& getEmittedEmitterList();

            virtual void clearEmittedEmitters();

            // Emitter
            virtual ParticleEmitter* createEmitter(const std::string &type="ParticleEmitter");

            virtual ParticleEmitter* getEmitter(const std::string &type);

            virtual void destroyEmitter(const std::string &type);

            virtual void destroyEmitters();

            virtual size_t getEmitterNum() const;

            // Affector
            virtual ParticleAffector* createAffector(const std::string &type);

            virtual ParticleAffector* getAffector(const size_t index);

            virtual void destroyAffector(const size_t index);

            virtual void destroyAffectors();

            virtual size_t getAffectorNum() const;

            virtual const AABB& getAABB() const;

            virtual void play();

            virtual void pause(const bool b=true);

            virtual void stop(const bool force=false);

            virtual bool isPlaying() const;

            virtual bool isPausing() const;

            virtual void tick(const real interval, const real step=DEFAULT_TICK_STEP);

            virtual void updateParticles(const Camera *pCamera);

            virtual void update();

            virtual void needUpdate(const bool forceParentUpdate=false);

            virtual void needUpdateAABB();

            virtual void updateAABB();

            virtual void clear();

            virtual ~ParticleSystem();

        protected:
            void _increaseParticlePool(const size_t size);

            void _increaseEmittedEmitterPool(const size_t size);

            void _destroyParticlePool();

            void _destroyEmittedEmitterPool();

            void _expireParticles(const real interval);

            void _expireEmittedEmitters(const real interval);

            void _triggerAffectors(const real interval);

            void _applyMotion(const real interval);

            void _triggerEmitters(const real interval);

            void _sortParticles(const Camera *pCamera);

            virtual void _updateAABB() const;

        private:
            ParticleSystem(const ParticleSystem &);

            ParticleSystem& operator = (const ParticleSystem &);

        protected:
            /** Sort by distance functor */
            struct SortByDistanceFunctor
            {
                /// Position to sort in
                Vector3 mSortPos;

                SortByDistanceFunctor(const Vector3& pos) : mSortPos(pos) {}
                float operator () (Particle* p) const
                {
                    return -(mSortPos - p->position).squaredLength();
                }
            };

            /** Sort by direction functor */
            struct SortByDirectionFunctor
            {
                /// Direction to sort in
                Vector3 mSortDir;

                SortByDirectionFunctor(const Vector3& dir) : mSortDir(dir) {}
                float operator () (Particle* p) const
                {
                    return mSortDir.dotProduct(p->position);
                }
            };

        protected:
            static RadixSort32<ParticleList, Particle*, float> RadixSorter;

        public:
            static const real DEFAULT_TICK_STEP;

        protected:
            ParticleVector mParticlePool;
            ParticleList mActiveParticles;
            ParticleList mFreeParticles;

            ParticleEmitterVector mEmittedEmitterPool;
            ParticleEmitterList mActiveEmittedEmitters;
            ParticleEmitterList mFreeEmittedEmitters;

            ParticleEmitterMap mEmitters;
            ParticleAffectorVector mAffectors;

            real mSpeed;
            bool mSortEnable;
            SortMode mSortMode;

            bool mEmitting;
            bool mPausing;
            mutable AABB mAABB;
            mutable bool mNeedUpdateAABB;
    };
}

#endif
